package com.paco.study.cloud.gateway.filter;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * ip防暴刷
 */
@Component
@RefreshScope
public class IpFilter implements GlobalFilter, Ordered, InitializingBean {

    @Value("${ip.max.in-1-minutes.cnt}")
    private Integer In1MCnt;
    @Value("${ip.max.in-5-minutes.cnt}")
    private Integer In5MCnt;

//    保存ip访问次数
    private static Map<Long, ConcurrentHashMap<String, Integer>> IP_MAP = new ConcurrentHashMap<>();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();

//        不是注册接口不处理
        if (!request.getPath().toString().startsWith("/api/user/register")) {
            return chain.filter(exchange);
        }

        ServerHttpResponse response = exchange.getResponse();

        // 从request对象中获取客户端ip
        InetSocketAddress remoteAddress = request.getRemoteAddress();
        if (remoteAddress == null) {
            return null;
        }
        String clientIp = remoteAddress.getHostString();

        long currentSec = System.currentTimeMillis() / 1000;

        //这里会加少，这里只取近似值
        ConcurrentHashMap<String, Integer> timeMap = IP_MAP.computeIfAbsent(currentSec, k -> new ConcurrentHashMap<>());
        Integer cnt = timeMap.getOrDefault(clientIp, 0);

        timeMap.put(clientIp, cnt + 1);

        long sum1 = calc(clientIp, currentSec, 60);
        long sum5 = calc(clientIp, currentSec, 60 * 5);

//        如果超过阈值，返回错误
        if (In1MCnt < sum1 || In5MCnt < sum5) {
            response.setStatusCode(HttpStatus.SEE_OTHER); // 状态码
            response.getHeaders().add("Content-Type", "text/html; charset=utf-8");
            String data = "您频繁进行注册，请求已被拒绝";
            DataBuffer wrap = null;
            wrap = response.bufferFactory().wrap(data.getBytes(StandardCharsets.UTF_8));
            return response.writeWith(Mono.just(wrap));
        }

        return chain.filter(exchange);
    }

//    统计次数
    private long calc(String clientIp, long currentSec, long beforeSec) {
        return IP_MAP.entrySet()
                .stream()
                .filter(ev -> ev.getKey() > (currentSec - beforeSec))
                .mapToLong(ev -> ev.getValue().get(clientIp))
                .filter(Objects::nonNull)
                .sum();
    }


    @Override
    public int getOrder() {
        return 0;
    }

    @Override
    public void afterPropertiesSet() {
        //        每分钟删除过期数据
        ScheduledExecutorService service = Executors
                .newSingleThreadScheduledExecutor();
        service.scheduleWithFixedDelay(() -> {
            long currentSec = System.currentTimeMillis() / 1000;
            for (Long key : IP_MAP.keySet()) {
//                删除十分钟之前的数据
                if (key - currentSec > 60 * 10) {
                    IP_MAP.remove(key);
                }
            }
        }, 1, 1, TimeUnit.MINUTES);
    }
}
